package za.co.smartmi.security;

import java.util.List;

import za.co.smartmi.util.ResourcesUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import za.co.smartmi.model.SystemAccount;

public class AuthenticationManagerImpl extends Authenticator implements AuthenticationManager {
	
	private Logger log = LoggerFactory.getLogger(AuthenticationManagerImpl.class);

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {

		SystemAccount account = null;
		
		if (authentication == null) {
			throw new BadCredentialsException(ResourcesUtil.getInstance().getProperty("login.username.password.req"));
		}
		
		if (authentication.getName() == null || authentication.getName().trim().length() == 0) {
			throw new BadCredentialsException(ResourcesUtil.getInstance().getProperty("login.username.password.req"));
		}
		
		if (authentication.getCredentials() == null || ((String)authentication.getCredentials()).trim().length() == 0) {
			throw new BadCredentialsException(ResourcesUtil.getInstance().getProperty("login.username.password.req"));
		}
		
		try {
			// Retrieve user details from database
			account = obtainAccount(authentication.getName());
		} catch (Exception e) {
			log.error("Could authenticate user due to: " + e.getMessage());
			throw new BadCredentialsException(e.getMessage());
		}
		
		if (account == null) {
			log.error("User not found for " + authentication.getName());
			throw new BadCredentialsException(ResourcesUtil.getInstance().getProperty("login.unsuccessful"));
		}
		
		// check credentials for found account
                
		if (!account.getSystemUser().isEnabled()) {
			throw new BadCredentialsException(ResourcesUtil.getInstance().getProperty("login.disabled"));
		}
		
		if (!isPasswordValid(account.getSystemUser().getPassword(), (String) authentication.getCredentials())) {
			log.error("Wrong password entered for account " + account.getSystemUser().getUsername());
			throw new BadCredentialsException(ResourcesUtil.getInstance().getProperty("login.unsuccessful"));
		}
		
		log.debug("User passed login validation = " + account.getSystemUser().getUsername());
		List<GrantedAuthority> authorities = buildGrantedAuthoritiesList(account.getSystemUser().getUsername());

		if(!hasSufficientAuthority(authorities)) {
			log.error("User " + account.getSystemUser().getUsername() + "does not have enough privileges to login");
			throw new BadCredentialsException(ResourcesUtil.getInstance().getProperty("login.insufficient.privileges"));
		}
		
		AuthenticatedUserDetails userDetails = buildUserDetails(authentication.getName(), (String)authentication.getCredentials(), 
															  account.getSystemUser().isEnabled(),
															  authorities);
															  
		return new UsernamePasswordAuthenticationToken(
				userDetails, 
				authentication.getCredentials(), 
				authorities);
	}

	private boolean hasSufficientAuthority(List<GrantedAuthority> authorities) {
		if (authorities == null || authorities.size() == 0) {
			return false;
		}
		
	    for (GrantedAuthority authority : authorities) {
	    	if (authority.getAuthority().equals("ROLE_SYSTEM_ADMINISTRATOR")) {
	    		return true;
	    	}
	    }
	    
	    return false;
		
	}
}
